package coupon

import (
	"context"
	"encoding/json"
	"fmt"
	"strings"
	"time"

	"mall/common/globalkey"
	"mall/common/modelx"
	"mall/common/moneyx"
	"mall/common/mqueuex"
	"mall/common/resultx"
	"mall/common/utils/pointy"
	enum "mall/service/backend/common/enum/promotion"
	"mall/service/promotion/model"
	"mall/service/promotion/rpc/internal/svc"
	"mall/service/promotion/rpc/types/promotion"

	"github.com/volatiletech/null/v8"
	"github.com/zeromicro/go-zero/core/logx"
)

type ReceiveCouponLogic struct {
	ctx    context.Context
	svcCtx *svc.ServiceContext
	logx.Logger
}

func NewReceiveCouponLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ReceiveCouponLogic {
	return &ReceiveCouponLogic{
		ctx:    ctx,
		svcCtx: svcCtx,
		Logger: logx.WithContext(ctx),
	}
}

func (l *ReceiveCouponLogic) ReceiveCoupon(in *promotion.ReceiveCouponReq) (*promotion.BaseResp, error) {
	if in.TemplateIds == nil || len(strings.TrimSpace(*in.TemplateIds)) == 0 {
		return nil, resultx.StatusError(resultx.COUPON_ID_REQUIRED, "")
	}

	if in.UserId == nil || *in.UserId == 0 {
		return nil, resultx.StatusError(resultx.USER_ID_REQUIRED, "")
	}

	userId := *in.UserId
	var activityId uint64
	if in.ActivityId != nil && *in.ActivityId > 0 {
		activityId = *in.ActivityId
	}

	ids := modelx.UniqueInterfaces(strings.TrimSpace(*in.TemplateIds), 64)
	if len(ids) == 0 {
		return nil, resultx.StatusError(resultx.COUPON_ID_REQUIRED, "")
	}

	filterMap := make(map[string]map[string][]any)
	filterMap[model.PromotionCouponTemplateColumns.ID] = map[string][]any{
		"in": ids,
	}
	list, _, err := l.svcCtx.CouponTemplate.GetList(l.ctx, model.M{}, -1, filterMap)
	if err != nil {
		return nil, resultx.StatusError(resultx.COUPON_ID_INVALID, "")
	}

	if len(list) > 0 {
		tx, err := l.svcCtx.DB.BeginTx(l.ctx, nil)
		if err != nil {
			return nil, resultx.StatusError(resultx.DB_ERROR, "")
		}

		var coupons []*mqueuex.CouponInfo
		var shouldSubmit bool
		for _, v := range list {
			if v.Status != uint16(globalkey.StatusStarted) {
				continue
			}

			if v.PerLimit > 0 {
				couponFilterMap := make(map[string]map[string][]any)
				couponFilterMap[model.PromotionCouponColumns.TemplateID] = map[string][]any{
					"equalTo": {v.ID},
				}
				couponFilterMap[model.PromotionCouponColumns.UserID] = map[string][]any{
					"equalTo": {userId},
				}
				_, total, err := l.svcCtx.Coupon.GetList(l.ctx, model.M{}, 0, couponFilterMap)
				if err != nil {
					return nil, resultx.StatusError(resultx.DB_ERROR, "")
				}

				if total >= int64(v.PerLimit) {
					continue
				}
			}

			data := &mqueuex.CouponInfo{
				TemplateId: v.ID,
				ActivityId: in.ActivityId,
				UserId:     userId,
			}
			now := time.Now()
			coupon := &model.PromotionCoupon{
				TemplateID: v.ID,
				UserID:     userId,
				CreatedAt:  now,
				UpdatedAt:  now,
			}
			if activityId > 0 {
				coupon.ActivityID = activityId
			}
			if v.ExpireMode == enum.PromotionCouponExpireDynamic {
				data.StartedAt = modelx.FormatTime(now, "")
				data.EndedAt = modelx.FormatTime(now.AddDate(0, 0, int(v.ExpireValue)).Add(time.Minute*-1), "")
				coupon.StartedAt = null.TimeFrom(now)
				coupon.EndedAt = null.TimeFrom(now.AddDate(0, 0, int(v.ExpireValue)).Add(time.Minute * -1))
				coupon.Status = uint16(globalkey.StatusStarted)
			} else if v.ExpireMode == enum.PromotionCouponExpireStatic {
				data.StartedAt = modelx.FormatNullDotTime(v.StartedAt, "")
				data.EndedAt = modelx.FormatNullDotTime(v.EndedAt, "")
				coupon.StartedAt = v.StartedAt
				coupon.EndedAt = v.EndedAt
				coupon.Status = uint16(globalkey.StatusStarted)
			}
			if v.GemPrice > 0 {
				data.Point = pointy.GetPointer(moneyx.FormatGem(int64(v.GemPrice)))
				coupons = append(coupons, data)
			} else {
				err = l.svcCtx.Coupon.Insert(l.ctx, tx, coupon)
				if err != nil {
					tx.Rollback()
					return nil, resultx.StatusError(resultx.DB_ERROR, "")
				}

				shouldSubmit = true
			}
		}

		if shouldSubmit {
			tx.Commit()
		}

		if len(coupons) > 0 {
			err = l.deductPoint(userId, coupons)
			if err != nil {
				logx.Errorw("ReceiveCoupon err", logx.Field("detail", err.Error()))
			}
		}
	}

	return &promotion.BaseResp{}, nil
}

// 积分扣减、领券逻辑
func (l *ReceiveCouponLogic) deductPoint(uid uint64, coupons []*mqueuex.CouponInfo) error {
	str, err := json.Marshal(coupons)
	if err != nil {
		return fmt.Errorf("ReceiveCoupon deductPoint json.Marshal error: %+v", err)
	}

	m := mqueuex.DistributePointsMessage{
		UserId:     pointy.GetPointer(uid),
		SourceType: pointy.GetPointer(globalkey.UserAssetSourceTypeCoupon),
		Coupons:    pointy.GetPointer(string(str)),
	}
	body, err := json.Marshal(m)
	if err != nil {
		return fmt.Errorf("ReceiveCoupon deductPoint json.Marshal error: %+v", err)
	}

	err = l.svcCtx.KqDistributePointsClient.Push(string(body))
	if err != nil {
		return fmt.Errorf("ReceiveCoupon deductPoint error: %+v", err)
	}

	return nil
}
